#ifndef  NODETERMINAL_CC_INC
#define  NODETERMINAL_CC_INC

#include	"node_terminal.h"
#include	"sim.h"

NodeTerminal::~NodeTerminal ()
{
}

void
NodeTerminal::parse_config(std::map<std::string,std::string>& p)
{
    // default
    ni_buffer_width = 4;
    no_nodes = 16;
    no_mcs = 0;

    // find MC_loc in p map and make a local copy
    std::map<std::string,std::string>::iterator it2;
    for ( it2 = p.begin(); it2 != p.end(); it2++)
    {
        string key = it2->first;
        string sim_string = key.substr(0,key.find(":"));
        if ( sim_string.compare("mc_loc") == 0 )
            mc_positions.push_back(atoi((it2->second).c_str()));
    }

    std::map<std::string,std::string>::iterator it;
    it = p.find("int_buff_width");
    if ( it != p.end())
        ni_buffer_width = atoi((it->second).c_str());
    it = p.find("no_nodes");
    if ( it != p.end())
        no_nodes = atoi((it->second).c_str());
    it = p.find("no_mcs");
    if ( it != p.end())
        no_mcs = atoi((it->second).c_str());

    return;
}

void
NodeTerminal::init()
{

    char tmp[30];
    sprintf(tmp,"log_%d.txt",Mytid);
    log_file = fopen (tmp, "a");

    stat_packets_in = 0;
    stat_packets_out = 0;
    stat_last_packet_out_cycle = 0;
    stat_last_packet_in_cycle = 0;
    stat_total_latency = 0;

    /* Can either set them to true or have the interface send you events */
    ni_buffers.resize(ni_buffer_width);
    ni_buffers.insert(ni_buffers.begin(), ni_buffers.size(), true);

    return;
}

void
NodeTerminal::handle_new_packet_event (int port, NetworkPacket* data)
{
    MreqPkt *mreqPkt = (MreqPkt*) data;

    proc_recv = false;
    /* Can this function be called twice in a cycle??
     * if it does make sure the interfacing code can handle it or make sure that it does not
     * happen*/

    stat_packets_in++;
    stat_last_packet_in_cycle = manifold::kernel::Manifold::NowTicks();

    uint lat = (manifold::kernel::Manifold::NowTicks() - mreqPkt->enter_network_time); //(sent_time part of pkt)
    stat_total_latency += lat;
    //    _DBG(" ********* NODETERMINAL got new pkt ********* vc %d %d",mreqPkt->proc_buffer_id, lat);
//    fprintf(stderr," ********* NODETERMINAL got new pkt ********* vc %d %d",mreqPkt->proc_buffer_id, lat);
//    Mreq* mreq = ((MreqPkt*)(mreqPkt))->mreq;
//    mreq->print_msg(mreq->src_mid,"Sending pkt to Node_to_iris_module!");

    handle_send_credit_event((int) SEND_SIG, mreqPkt->proc_buffer_id);

    // get mreq and call write_output_port of node_to_iris_module
//    mreqPkt->mreq->print_msg(mreqPkt->mreq->src_mid,"node_terminal eject");
    caffTerm->write_output_port(mreqPkt->mreq, Global_Clock);

    delete data;
    return;
}

std::string
NodeTerminal::print_stats( void ) const
{
    std::stringstream str;
    str << "\n NodeTerminal[" << node_id << "] packets_out: " << stat_packets_out
        << "\n NodeTerminal[" << node_id << "] packets_in: " << stat_packets_in
        << "\n NodeTerminal[" << node_id << "] avg_latency: " << (stat_total_latency+0.0)/stat_packets_in
        << "\n NodeTerminal[" << node_id << "] stat_last_packet_out_cycle: " << stat_last_packet_out_cycle
        << "\n NodeTerminal[" << node_id << "] stat_last_packet_in_cycle: " << stat_last_packet_in_cycle
        << std::endl;
    return str.str();
}

void
NodeTerminal::handle_issue_pkt_event(int port)
{
    // see function that takes a Mreq
    return;
}

void
NodeTerminal::handle_issue_pkt_event(Mreq* mreq)
{

    stat_packets_out++;
    stat_last_packet_out_cycle = manifold::kernel::Manifold::NowTicks();

    MreqPkt* np = new MreqPkt();
    np->src_node = node_id; //this->GetComponentId();
    //uint tmp = (node_id + no_nodes/2 -1)%no_nodes;
    uint tmp = -1;
    tmp = (node_id + 2)%no_nodes;

    assert ( tmp < no_nodes );
    np->dst_node = mreq->dest_mid.nodeID; //node_id ? (tmp++)%no_nodes : tmp;
    np->dst_component_id = 0;       // can use this for further splitting at the interface
    np->address = mreq->addr; // gsl_ran_gaussian_tail( addr_gen ,0,2);
    np->mclass = PROC_REQ;
    np->proc_buffer_id = send_buffer_id;
    np->payload_length = 0;
    np->enter_network_time = manifold::kernel::Manifold::NowTicks();
    np->mreq = mreq;

    ni_buffers[send_buffer_id] = false;

    if( mreq->dest_mid.nodeID == 0)
    {
        np->mclass = MC_RESP;
    }
#ifdef _DEBUG2
    mreq->print_msg(mreq->src_mid,"node_terminal inject");
    cerr << " sendbuff:" << send_buffer_id << endl;
    //_DBG("pktgen pkt out: vc%d dst:%d src:%d", send_buffer_id, np->dst_node, np->src_node);
#endif

    manifold::kernel::Manifold::Schedule( 1 , &IrisInterface::handle_new_packet_event,
                                          static_cast<IrisInterface*>(ni), (int)SEND_DATA, np);
    //        Send(SEND_DATA,np);


    return;
}

void
NodeTerminal::handle_send_credit_event(int port, uint64_t data)
{
    /* Send ack to interface */
    assert( ni != NULL );
    manifold::kernel::Manifold::Schedule( 1, &IrisInterface::handle_update_credit_event, 
                                          static_cast<IrisInterface*>(ni), data);

    return;
}

void
NodeTerminal::handle_update_credit_event(int p, uint64_t credit)
{
    assert( credit < ni_buffer_width );
    ni_buffers[credit] = true;

    /* 
       printf("\nT:%lld N:%d got credit for vc %d credits:",manifold::kernel::Manifold::NowTicks(), node_id, credit);
       for ( uint i=0; i<ni_buffer_width; i++)
       {
       if ( ni_buffers[i] )
       printf(" 1");
       else
       printf(" 0");
       }
     * */
    return;
}

void
NodeTerminal::tock()
{
    //    handle_issue_pkt_event(SEND_DATA);
    return;
}

void
NodeTerminal::tick ( void )
{
    return ;
}

#endif   /* ----- #ifndef NODETERMINAL_CC_INC  ----- */

